﻿using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using MQTT.Server.Adapter;
using MQTT.Share.Extension;
using MQTTnet.Adapter;
using MQTTnet.Diagnostics;
using MQTTnet.Implementations;

namespace MQTT.Server.Extension
{
    public static class MqttExtension
    {

        public static IServiceCollection AddMQTT(this IServiceCollection services)
        {
            var logger = new MqttNetLogger();
            services.AddSingleton<IMqttNetLogger>(logger);

            var childLogger = logger.CreateChildLogger();
            services.AddSingleton(childLogger);

            return services;
        }

        public static IServiceCollection AddMqttWebSocketServerAdapter(this IServiceCollection services)
        {

           
            services.AddSingleton<MqttWebSocketServerAdapter>();
            services.AddSingleton<IMqttServerAdapter>(s => s.GetService<MqttWebSocketServerAdapter>());

            return services;
        }

        public static IServiceCollection AddMqttTcpServerAdapter(this IServiceCollection services)
        {
            services.AddSingleton<MqttTcpServerAdapter>();
            services.AddSingleton<IMqttServerAdapter>(s => s.GetService<MqttTcpServerAdapter>());

            return services;
        }


        public static IApplicationBuilder UseWebsocketMQTTServer(this IApplicationBuilder app,
            IConfiguration Configuration)
        {

            app.UseMQTTServer(Configuration);
            string path = Configuration["MQTT:WebSocket:Path"] ?? "/mqtt";
            app.UseWebSockets();
            app.Use(async (context, next) =>
            {
                if (!context.WebSockets.IsWebSocketRequest || context.Request.Path != path)
                {
                    await next();
                    return;
                }

                string subProtocol = null;

                if (context.Request.Headers.TryGetValue("Sec-WebSocket-Protocol", out var requestedSubProtocolValues))
                {
                    subProtocol = SelectSubProtocol(requestedSubProtocolValues);
                }

                var adapter = app.ApplicationServices.GetRequiredService<MqttWebSocketServerAdapter>();
                using (var webSocket = await context.WebSockets.AcceptWebSocketAsync(subProtocol))
                {
                    var endpoint = $"{context.Connection.RemoteIpAddress}:{context.Connection.RemotePort}";
                    await adapter.RunWebSocketConnectionAsync(webSocket, endpoint);
                }
            });
            return app;
        }


        public static string SelectSubProtocol(IList<string> requestedSubProtocolValues)
        {
            // Order the protocols to also match "mqtt", "mqttv-3.1", "mqttv-3.11" etc.
            return requestedSubProtocolValues
                .OrderByDescending(p => p.Length)
                .FirstOrDefault(p => p.ToLower().StartsWith("mqtt"));
        }
    }
}
